﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;

namespace ClassDataExport
{
    /// <summary>
    /// Ausgabeklasse, welches die übergebenen PropertyValues von T in das jeweilige Format "übersetzt" z.b. csv oder dataTable
    /// </summary>
    public class DataOutput<T>
    {
        #region Member
        private const char Seperator = ';';
        #endregion

        #region Properties
        /// <summary>
        /// PropertiesValue Collection aus der die Daten ausgelesen werden
        /// </summary>
        public List<PropertyValue<T>> PropertyValues { get; set; }

        /// <summary>
        /// Auflistung der Ausgabeproperties welche Properties ausgegeben wurden
        /// </summary>
        public SortedList<int, OutputProperties> OutputProperties { get; set; }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Initialisieren des DataOutputs
        /// </summary>
        /// <param name="outputProperties">Auflistung der Properties die ausgegeben wurden</param>
        public DataOutput(SortedList<int, OutputProperties> outputProperties)
        {
            OutputProperties = outputProperties;
        }
        #endregion

        #region Functions / Subs
        /// <summary>
        /// Erstellen einer Exceldatei und ablegen in dem übergebenen Filepath, der Dateipfad muss bereits existieren, die Datei nicht!
        /// </summary>
        /// <param name="filePath">Dateipfad inkl. Dateiname</param>
        /// <returns>TRUE->Erstellen erfolgreich | FALSE->Fehler beim Erstellen</returns>
        public Boolean CreateExcelFile(string filePath)
        {
            //TODO Header setzten im Excelfile
            return ExcelExport.GetExcelExport().CreateExcelFileFromDataTable(GetDataTable(), filePath);
        }

        /// <summary>
        /// Gibt eine DataTable mit den Daten zurück die angezeigt werden sollen
        /// </summary>
        /// <returns>DataTable mit den Daten der Klasse</returns>
        public DataTable GetDataTable()
        {
            //Auslesen der Struktur 
            DataTable dataTable = CreateDataTable();
            //Alle Datensätze durchgehen un die DataTable mit Daten füllen
            foreach (PropertyValue<T> eItems in PropertyValues)
            {
                //Erstellen einer neuen DataRow
                DataRow dataRow = dataTable.NewRow();

                //Alle Properties des aktuellen Items durchgehen und dessen Inhalt ausgeben
                foreach (KeyValuePair<int, ClassData> ePropertyValues in eItems.PropertyValues)
                {
                    dataRow[ePropertyValues.Value.PropertyName] = ePropertyValues.Value.PropertyValue;
                }

                dataTable.Rows.Add(dataRow);
            }

            return dataTable;
        }

        /// <summary>
        /// Erstellt das CSV File in dem übergebenen Pfad inkl. Dateiname
        /// </summary>
        /// <param name="filePath">Dateipfad wo die csv Datei angelegt werden soll inkl. dateiname</param>
        /// <returns>TRUE->Erstellen erfolgreich | FALSE->Fehler beim Erstellen</returns>
        public Boolean GetCSV(string filePath)
        {
            //Prüfen ob der Pfad auch existiert
            if (Directory.Exists(Path.GetDirectoryName(filePath)))
            {
                try
                {
                    File.WriteAllText(filePath, GetCSV());
                    return true;
                }
                catch (Exception exception)
                {
                    SQDebugging.Debugging.WriteLog(exception);
                }
            }

            return false;
        }

        /// <summary>
        /// Heraussuchen aller Werte PropertyValues der übergebenen Items T
        /// </summary>
        /// <returns>CVS String mit allen Values</returns>
        public string GetCSV()
        {
            //TODO noch einbauen das der Seperator übergeben werden kann, hier muss dann in den Strings überprüft werden ob der Seperator vorkommt oder nicht.

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(GetCSVHeader());
            //Alle gefundenen Items durchgehen die hinterlegt wurden
            foreach (PropertyValue<T> eItems in PropertyValues)
            {
                //Alle Properties des aktuellen Items durchgehen und dessen Inhalt ausgeben
                foreach (KeyValuePair<int, ClassData> ePropertyValues in eItems.PropertyValues)
                {
                    stringBuilder.Append(ePropertyValues.Value.PropertyValue).Append(Seperator);
                }

                //Nach jedem Item einen Zeilenumbruch einbauen
                stringBuilder.Append("\r\n");
            }

            return stringBuilder.ToString();
        }

        /// <summary>
        /// Gibt einen CSV Header zurück mit den passenden Bezeichnungen
        /// </summary>
        /// <returns>CSV Header String mit den Spaltenbezeichnungen</returns>
        private string GetCSVHeader()
        {
            string header = string.Empty;
            int count = 0;

            //Die Auflistung mit den OutputProperties durchgehen und danach prüfen ob das Property auch ausgegeben wird
            foreach (KeyValuePair<int, OutputProperties> eOutputPropertiese in OutputProperties)
            {
                foreach (PropertyValue<T> ePropertyValue in PropertyValues)
                {
                    //In der Propertycollection suchen ob das Property auch ausgegeben werden soll
                    foreach (KeyValuePair<int, ClassData> eClassItem in ePropertyValue.PropertyValues)
                    {
                        //Nur wenn das Property auch in der Ausgabeliste enthalten ist, dieses im Header mit ausgeben
                       if (eOutputPropertiese.Value.PropertyName == eClassItem.Value.PropertyName)
                       {
                           //Muss mittels count gelöst werden, da auch leerwerte im Header stehen können
                           if (count > 0)
                           {
                               header += Seperator;
                           }

                           //Hinzufügen des Wertes zur Headerbezeichnung
                           header += eOutputPropertiese.Value.ColumnHeaderName;
                           count++;
                           break;
                       }
                    }

                    break;
                }
            }

            if (header.Trim().Length > 0)
            {
                header += "\r\n";
            }

            return header;
        }

        /// <summary>
        /// Erstellt das Grundgerüst der DataTable, welches dann mit Daten gefüllt werden kann
        /// </summary>
        /// <returns>Basic DataTable die mit Daten gefüllt werden kann.</returns>
        private DataTable CreateDataTable()
        {
            // Create a new DataTable
            DataTable dataTable = new DataTable();

            //evtl. später nur den ersten Datensatz nehmen und daraus auslesen welche Spalten erstellt werden sollen
            //im moment wäre es möglich das einzelne Items mehr Spalten zurückgeben und das dann auch nur in diesen
            //die Werte angegeben werden
            foreach (PropertyValue<T> eColumn in PropertyValues)
            {
                //Als Tabellennamen, den namen der Klasse eintragen wo die Properties ausgelesen werden
                if (dataTable.TableName.Length == 0)
                {
                    dataTable.TableName = eColumn.Item.GetType().Name;
                }

                foreach (KeyValuePair<int, ClassData> eClassData in eColumn.PropertyValues)
                {
                    //Die Spalten nur hinzufügen, wenn diese noch nicht existiert
                    if (!dataTable.Columns.Contains(eClassData.Value.PropertyName))
                    {
                        //TODO evtl. die originaldatentypen nehmen und der DataTable hinzufügen
                        //Erstellen der Spalte, damit diese später gefüllt werden kann
                        DataColumn column = new DataColumn();
                        column.DataType = typeof(string);
                        column.DefaultValue = string.Empty;
                        column.ColumnName = eClassData.Value.PropertyName;
                        dataTable.Columns.Add(column);
                    }
                }
            }

            return dataTable;
        }
        #endregion
    }
}
